package ljl.bilibili.chat.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import ljl.bilibili.chat.entity.NoticeCount;
import ljl.bilibili.chat.entity.PPTWord;
import ljl.bilibili.chat.handler.PPTHandler;
import ljl.bilibili.chat.mapper.ChatServiceMapper;
import ljl.bilibili.chat.ppt.*;
import ljl.bilibili.chat.service.ChatService;
import ljl.bilibili.chat.vo.request.ChangeChatStatusRequest;
import ljl.bilibili.chat.vo.request.ChatSessionRequest;
import ljl.bilibili.chat.vo.response.ChatSessionResponse;
import ljl.bilibili.chat.vo.response.HistoryChatResponse;
import ljl.bilibili.chat.vo.response.PPTResponse;
import ljl.bilibili.chat.vo.response.TempSessionResponse;
import ljl.bilibili.entity.chat.Chat;
import ljl.bilibili.entity.chat.ChatSession;
import ljl.bilibili.entity.user_center.user_info.User;
import ljl.bilibili.mapper.chat.ChatMapper;
import ljl.bilibili.mapper.chat.ChatSessionMapper;
import ljl.bilibili.mapper.user_center.user_info.UserMapper;
import ljl.bilibili.util.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static ljl.bilibili.chat.constant.Constant.*;

/**
 * 聊天service
 */
@Service
@Slf4j
public class ChatServiceImpl implements ChatService {
    @Resource
    ChatSessionMapper chatSessionMapper;
    @Resource
    ChatMapper chatMapper;
    @Resource
    UserMapper userMapper;
    @Resource
    ChatServiceMapper chatServiceMapper;

    /**
     * 修改聊天状态
     * <p>
     * 此方法用于更新聊天记录的状态它主要用于将特定聊天记录标记为已读
     * 聊天记录是由发送方ID和接收方ID共同确定的此方法将聊天状态设置为1，
     * 通常表示聊天记录已读
     *
     * @param changeChatStatusRequest 包含需要更改聊天状态的用户信息和接收方信息的请求对象
     * @return 返回一个包含操作结果的Result对象，如果操作成功，将返回true
     */
    @Override
    public Result<Boolean> changeChatStatus(ChangeChatStatusRequest changeChatStatusRequest) {
        // 创建一个LambdaUpdateWrapper对象，用于构建更新条件
        LambdaUpdateWrapper<Chat> wrapper = new LambdaUpdateWrapper<>();
        // 设置聊天状态为1，通常表示已读
        wrapper.set(Chat::getStatus, 1);
        // 设置更新条件为：发送方ID等于接收方ID
        wrapper.eq(Chat::getSenderId, changeChatStatusRequest.getReceiverId());
        // 设置更新条件为：接收方ID等于用户ID
        wrapper.eq(Chat::getReceiverId, changeChatStatusRequest.getUserId());
        // 执行更新操作，参数为null表示更新所有列
        chatMapper.update(null, wrapper);
        // 返回操作结果，成功返回true
        return Result.success(true);
    }


    /**
     * 创建临时会话
     * 根据接收者的ID获取用户信息，并生成临时会话响应对象
     *
     * @param receiverId 接收者的用户ID，用于查询用户信息
     * @return 返回包含用户封面和昵称的临时会话响应对象
     */
    @Override
    public Result<TempSessionResponse> createTempSession(Integer receiverId) {
        // 根据接收者的ID查询用户信息
        User u = userMapper.selectById(receiverId);
        // 创建并返回临时会话响应对象，包含用户的封面和昵称
        return Result.data(new TempSessionResponse().setCover(u.getCover()).setNickName(u.getNickName()));
    }


    /**
     * 修改回话的最后聊天时间和内容
     */
    @Override
    public Result<Boolean> changeChatSessionTime(ChatSessionRequest chatSessionRequest) {
        // 创建LambdaUpdateWrapper对象，用于构建更新条件和设置更新的字段
        LambdaUpdateWrapper<ChatSession> wrapper = new LambdaUpdateWrapper<>();
        // 设置更新条件：根据发送方ID匹配
        wrapper.eq(ChatSession::getSenderId, chatSessionRequest.getSenderId());
        // 设置更新条件：根据接收方ID匹配
        wrapper.eq(ChatSession::getReceiverId, chatSessionRequest.getReceiverId());
        // 设置更新的字段：更新时间设置为当前时间
        wrapper.set(ChatSession::getUpdateTime, LocalDateTime.now());
        // 设置更新的字段：更新内容设置为请求对象中的更新内容
        wrapper.set(ChatSession::getUpdateContent, chatSessionRequest.getUpdateContent());
        // 执行更新操作，参数为null表示更新所有匹配的记录，不需要额外的查询条件
        chatSessionMapper.update(null, wrapper);
        // 返回一个成功的Result对象，表示更新操作完成
        return Result.success(true);
    }

    /**
     * 获取用户的历史会话列表（包含自有会话和他人共享会话）
     * <p>
     * 实现逻辑：
     * 1. 合并自有会话和他人共享会话
     * 2. 统计每个会话的未读消息数并设置状态
     * 3. 按更新时间倒序排序
     *
     * @param userId 用户唯一标识符，不能为null
     * @return 包含历史会话列表的Result对象，会话数据包含：
     * - 会话基础信息
     * - 未读消息数量
     * - 会话状态标识
     */
    @Override
    public Result<List<ChatSessionResponse>> getHistoryChatSession(Integer userId) {
        // 合并两种来源的会话数据：用户自己创建的会话和他人共享的会话
        List<ChatSessionResponse> selfResponses = chatServiceMapper.getSelfSession(userId);
        List<ChatSessionResponse> otherResponses = chatServiceMapper.getOtherSession(userId);
        selfResponses.addAll(otherResponses);
        // 准备会话ID集合用于批量查询未读消息
        List<Integer> idList = new ArrayList<>();
        for (ChatSessionResponse sessionResponse : selfResponses) {
            idList.add(sessionResponse.getSessionId());
        }
        // 当存在有效会话时处理未读消息状态
        if (idList.size() > 0) {
            // 批量获取会话未读数（数据库层面聚合计算）
            List<NoticeCount> noticeCounts = chatServiceMapper.getNoticeCounts(idList, userId);
            // 双重循环匹配设置会话未读状态（时间复杂度O(n^2)，在数据量可控范围内适用）
            for (ChatSessionResponse sessionResponse : selfResponses) {
                for (NoticeCount noticeCount : noticeCounts) {
                    if (noticeCount.getSessionId().equals(sessionResponse.getSessionId())) {
                        if (noticeCount.getNoticeCount() > 0) {
                            sessionResponse.setCount(noticeCount.getNoticeCount());
                            sessionResponse.setStatus(false);  // 存在未读时设置状态为未读
                            break;
                        }
                    }
                }
            }
        }
        // 按会话最后更新时间进行降序排列（最新修改的会话排最前）
        if (selfResponses.size() > 0) {
            selfResponses = selfResponses.stream()
                    .sorted(Comparator.comparing(ChatSessionResponse::getUpdateTime).reversed())
                    .collect(Collectors.toList());
        }
        return Result.data(selfResponses);
    }

    /**
     * 新增聊天会话和内容
     */
    @Override
    public Result<Boolean> addChatSessionAndContent(ChatSessionRequest chatSessionRequest) {
        // 查询之前是否存在自己向他人或他人向自己发起的会话
        // 构建查询条件1：发送方ID等于发送方ID，接收方ID等于接收方ID
        LambdaQueryWrapper<ChatSession> wrapper1 = new LambdaQueryWrapper<>();
        wrapper1.eq(ChatSession::getSenderId, chatSessionRequest.getSenderId());
        wrapper1.eq(ChatSession::getReceiverId, chatSessionRequest.getReceiverId());
        // 构建查询条件2：发送方ID等于接收方ID，接收方ID等于发送方ID
        LambdaQueryWrapper<ChatSession> wrapper2 = new LambdaQueryWrapper<>();
        wrapper2.eq(ChatSession::getSenderId, chatSessionRequest.getReceiverId());
        wrapper2.eq(ChatSession::getReceiverId, chatSessionRequest.getSenderId());
        // 执行查询，获取符合条件的会话记录
        ChatSession c1 = chatSessionMapper.selectOne(wrapper1);
        ChatSession c2 = chatSessionMapper.selectOne(wrapper2);
        // 将请求参数转换为会话实体对象，并设置更新时间
        ChatSession chatSession = chatSessionRequest.toSessionEntity();
        chatSession.setUpdateTime(LocalDateTime.now());
        // 如果查询结果为空，表示该会话之前并未存在，则插入新的会话记录
        if (c1 == null && c2 == null) {
            chatSessionMapper.insert(chatSession);
        } else {
            // 如果会话已存在，则更新会话内容和时间
            chatServiceMapper.updateChatSession(chatSessionRequest.getUpdateContent(), LocalDateTime.now(), chatSessionRequest.getSenderId(), chatSessionRequest.getReceiverId());
        }
        // 插入聊天内容记录
        chatMapper.insert(chatSessionRequest.toChatEntity().setContent(chatSession.getUpdateContent()));
        // 返回操作成功的结果
        return Result.success(true);
    }

    /**
     * 获取用户聊天历史
     *
     * @param userId 用户ID，用于区分不同的用户
     * @param receiverId 接收者ID，用于区分用户之间的对话
     * @return 返回包含聊天历史的Result对象
     *
     * 该方法首先通过两个查询条件分别获取用户发送和接收的聊天记录，
     * 然后将这两部分记录合并，并转换为HistoryChatResponse对象，
     * 最后按创建时间降序排序并返回
     */
    @Override
    public Result<List<HistoryChatResponse>> getHistoryChat(Integer userId, Integer receiverId) {
        // 查询用户发送的聊天记录
        LambdaQueryWrapper<Chat> wrapper1 = new LambdaQueryWrapper<>();
        wrapper1.eq(Chat::getSenderId, userId);
        wrapper1.eq(Chat::getReceiverId, receiverId);
        // 查询用户接收的聊天记录
        LambdaQueryWrapper<Chat> wrapper2 = new LambdaQueryWrapper<>();
        wrapper2.eq(Chat::getReceiverId, userId);
        wrapper2.eq(Chat::getSenderId, receiverId);
        // 执行查询获取列表
        List<Chat> list1 = chatMapper.selectList(wrapper1);
        List<Chat> list2 = chatMapper.selectList(wrapper2);
        // 合并发送和接收的聊天记录
        list1.addAll(list2);
        // 初始化响应列表
        List<HistoryChatResponse> responses = new ArrayList<>();
        for (Chat chat : list1) {
            responses.add(new HistoryChatResponse(chat));
        }
        // 按创建时间降序排序聊天记录
        responses = responses.stream()
                .sorted(Comparator.comparing(HistoryChatResponse::getCreateTime).reversed())
                .collect(Collectors.toList());
        // 返回整理后的聊天历史
        return Result.data(responses);
    }


    /**
     * 根据描述生成PPT响应对象
     * 该方法通过调用讯飞星火API生成PPT大纲和详细内容，并将其封装成PPTResponse对象返回
     *
     * @param describe 描述信息，用于生成PPT大纲
     * @return PPTResponse对象，包含生成的PPT信息
     * @throws IOException 当文件操作或网络通信发生错误时抛出
     */
    public PPTResponse getPPT(String describe) throws IOException {
        // 获取当前时间戳，用于鉴权
        long timestamp = System.currentTimeMillis() / 1000;
        String ts = String.valueOf(timestamp);
        // 获得鉴权信息
        ApiAuthAlgorithm auth = new ApiAuthAlgorithm();
        String signature = auth.getSignature(appId, apiSecret, timestamp);
        // 初始化API客户端
        ApiClient client = new ApiClient("https://zwapi.xfyun.cn");
        // 大纲生成
        String outlineResp = client.createOutline(appId, ts, signature, describe);
        CreateResponse outlineResponse = JSON.parseObject(outlineResp, CreateResponse.class);
        // 使用ObjectMapper将JSON数据解析为OutlineVo对象
        ObjectMapper objectMapper = new ObjectMapper();
        OutlineVo outlineVo = objectMapper.readValue(outlineResponse.getData().getOutline(), OutlineVo.class);
        // 初始化章节列表和章节映射，用于后续的内容扩展
        List<String> chapterList = new ArrayList<>();
        Map<String, Object> chapterMap = new HashMap<>();
        int i = 1, j = 1;
        // 构建PPT前缀信息，包含标题和副标题
        String prefix = "主题是" + outlineVo.getTitle() + ",副主题是" + outlineVo.getSubTitle() + ",大纲是";
        // 遍历大纲章节，构建章节标题字符串
        for (OutlineVo.Chapter chapter : outlineVo.getChapters()) {
            prefix = prefix + chapter.chapterTitle + ",";
            if (chapter.chapterContents.size() > 0) {
                for (OutlineVo.Chapter chapter1 : chapter.chapterContents) {
                    prefix = prefix + chapter1.chapterTitle + ",";
                }
            }
        }
        // 再次遍历大纲章节，进行内容扩展
        for (OutlineVo.Chapter chapter : outlineVo.getChapters()) {
            chapterMap.put(String.valueOf(i), chapter.chapterTitle);
            chapterList.add(String.valueOf(i));
            for (OutlineVo.Chapter chapter1 : chapter.chapterContents) {
                chapterList.add(i + "." + j);
                PPTHandler pptHandler = new PPTHandler(chapter1.chapterTitle, appId, apiKey, apiSecret);
                chapterMap.put((i + "." + j), pptHandler);
                j++;
                try {
                    pptHandler.send(prefix + "现在的详细解说内容是:" + chapter1.chapterTitle);
                    while (pptHandler.pptDetail.getGenerateEnding() == false) {
                        Thread.sleep(200);
                        log.info(pptHandler.pptDetail.getThemeName() + "还没打印完");
                    }
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
            i++;
            j = 1;
        }
        // 将扩展后的PPT大纲和详情封装到PPTWord对象列表中
        List<PPTWord> pptWords = new ArrayList<>();
        Pattern pattern = Pattern.compile("^[1-9]$");
        for (String index : chapterList) {
            if (pattern.matcher(index).matches()) {
                pptWords.add(new PPTWord().setIndex(index).setThemeName((String) chapterMap.get(index)));
            } else {
                PPTHandler pptHandler = (PPTHandler) chapterMap.get(index);
                pptWords.add(new PPTWord().setIndex(index).setThemeName(pptHandler.pptDetail.getThemeName()).setText(pptHandler.pptDetail.getText()));
            }
            // 打印日志信息
            if (chapterMap.get(index) instanceof String) {
                log.info((String) chapterMap.get(index));
            } else {
                PPTHandler pptHandler = (PPTHandler) chapterMap.get(index);
                log.info(pptHandler.pptDetail.getText());
            }
        }
        // 构建并返回PPTResponse对象
        return new PPTResponse().setPptWordList(pptWords).setCoverImgSrc(outlineResponse.getData().getCoverImgSrc());
    }


    /**
     * 获取讯飞星火图片响应
     */
    public String getImage(String text) throws Exception {
        //绑定图片响应地址
        String url = "https://spark-api.cn-huabei-1.xf-yun.com/v2.1/tti";

        String authUrl = MyUtil.getAuthUrl(url, apiKey, apiSecret);
        //特定json格式发送请求到讯飞星火
        String json = "{\n" +
                "  \"header\": {\n" +
                "    \"app_id\": \"" + appId + "\",\n" +
                "    \"uid\": \"" + UUID.randomUUID().toString().substring(0, 15) + "\"\n" +
                "  },\n" +
                "  \"parameter\": {\n" +
                "    \"chat\": {\n" +
                "      \"domain\": \"s291394db\",\n" +
                "      \"temperature\": 0.5,\n" +
                "      \"max_tokens\": 4096,\n" +
                "      \"width\": 1024,\n" +
                "      \"height\": 1024\n" +
                "    }\n" +
                "  },\n" +
                "  \"payload\": {\n" +
                "    \"message\": {\n" +
                "      \"text\": [\n" +
                "        {\n" +
                "          \"role\": \"user\",\n" +
                "           \"content\": \"" + text + "\"\n" +
                "        }\n" +
                "      ]\n" +
                "    }\n" +
                "  }\n" +
                "}";
        //获取响应的Base64编码
        String res = MyUtil.doPostJson(authUrl, null, json);
        return res;
    }
}
